package pers.jing.android.cmd.tools

import com.android.repository.api.*
import com.android.repository.util.InstallerUtil
import com.android.sdklib.repository.AndroidSdkHandler
import com.android.sdklib.repository.installer.SdkInstallerUtil
import com.android.sdklib.tool.sdkmanager.SdkManagerCli
import com.google.common.collect.HashMultimap
import com.google.common.collect.Multimap
import java.util.Collections
import java.util.TreeSet
import java.util.function.Consumer

/**
 * 安装工具
 */
class InstallAction(
    val settings: SettingsController,
                    val androidSdkHandler: AndroidSdkHandler,
                    val downloader: Downloader,
                    val progress: ProgressIndicator,
    val packages: Array<String>) {

    val repoManager: RepoManager = androidSdkHandler.getSdkManager(progress)


    @Throws(SdkManagerCli.CommandFailedException::class)
    fun execute() {
        progress.setText("Loading package information...")
        repoManager.loadSynchronously(0L, progress.createSubProgress(0.1), downloader, settings)
        var remotes: MutableList<RemotePackage>? = mutableListOf()

        for (path in packages) {
            val p = this.repoManager.packages.remotePackages[path]
            if (p == null) {
                progress.logWarning("Failed to find package '$path'")
                throw SdkManagerCli.CommandFailedException()
            }
            remotes!!.add(p)
        }

        remotes = InstallerUtil.computeRequiredPackages(remotes, repoManager.packages, progress)
        if (remotes != null) {
            val acceptedRemotes = this.checkLicenses(remotes, progress)
            if (acceptedRemotes != remotes) {
                println("The following packages can not be installed since their licenses or those of the packages they depend on were not accepted:")
                remotes.stream().filter { px: RemotePackage? -> !acceptedRemotes.contains(px) }
                    .forEach { px: RemotePackage? ->
                        println("  " + px!!.path)
                    }
                if (!acceptedRemotes.isEmpty()) {
                    print("Continue installing the remaining packages? (y/N): Auto y")
                }
                remotes = acceptedRemotes
            }

            var progressMax = 0.1
            val progressIncrement = 0.9 / remotes.size.toDouble()

            for (p in remotes) {
                progress.setText("Installing " + p.displayName)
                val installer = SdkInstallerUtil.findBestInstallerFactory(p, androidSdkHandler)
                    .createInstaller(p, repoManager, downloader)
                progressMax += progressIncrement
                if (!this.applyPackageOperation(installer, progress.createSubProgress(progressMax))) {
                    throw SdkManagerCli.CommandFailedException()
                }

                progress.fraction = progressMax
            }

            progress.fraction = 1.0
        } else {
            progress.logWarning("Unable to compute a complete list of dependencies.")
            throw SdkManagerCli.CommandFailedException()
        }
    }

    private fun checkLicenses(
        remotes: MutableList<RemotePackage>,
        progress: ProgressIndicator?
    ): MutableList<RemotePackage> {
        var remotes = remotes
        val unacceptedLicenses: Multimap<License?, RemotePackage?> = HashMultimap.create<License?, RemotePackage?>()
        remotes.forEach(Consumer { remote: RemotePackage? ->
            val l = remote?.license
            if (l != null && !l.checkAccepted(androidSdkHandler.location)) {
                unacceptedLicenses.put(l, remote)
            }
        })

        unacceptedLicenses.keySet()
        for (l in TreeSet(unacceptedLicenses.keySet())) {
            //自动接受
            unacceptedLicenses.removeAll(l)
            l?.setAccepted(repoManager.localPath)
        }

        if (!unacceptedLicenses.isEmpty()) {
            val acceptedPackages: MutableList<RemotePackage> = remotes.toMutableList()
            val problemPackages: MutableSet<RemotePackage?> = unacceptedLicenses.values().toMutableSet()
            println("Skipping following packages as the license is not accepted:")
            problemPackages.forEach(Consumer { problem: RemotePackage? ->
                println(problem!!.displayName)
            })
            acceptedPackages.removeAll(problemPackages)
            val acceptedIter = acceptedPackages.iterator()

            while (acceptedIter.hasNext()) {
                val accepted = acceptedIter.next()
                val required = InstallerUtil.computeRequiredPackages(
                    mutableListOf<RemotePackage?>(accepted),
                    repoManager.packages,
                    progress
                )
                if (!Collections.disjoint(required, problemPackages)) {
                    acceptedIter.remove()
                    problemPackages.add(accepted)
                }
            }

            remotes = acceptedPackages
        }

        return remotes
    }


    fun applyPackageOperation(operation: PackageOperation, progress: ProgressIndicator): Boolean {
        return operation.prepare(progress.createSubProgress(0.5)) && operation.complete(progress.createSubProgress(1.0))
    }
}